﻿using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Collections.Generic;
using System.Reflection;
using System.Windows.Forms;
using PMC;
using PMC.Scripts;
using PMC.Structs;

namespace PMCgui
{
    public class Comparer<T> : IComparer<T>
    {
        private string compare;

        public Comparer(string compare)
        {
            this.compare = compare;
        }

        #region Implementation of IComparer<in T>

        public int Compare(T x, T y)
        {
            var prop = typeof (T).GetProperty(compare);
            var left = Convert.ToString(prop.GetValue(x, null));
            var right = Convert.ToString(prop.GetValue(y, null));

            return string.Compare(left, right);
        }

        #endregion
    }

    /// <summary>
    /// Usercontrol voor het tonen van gegevens in tabellen
    /// </summary>
    /// <typeparam name="T">Het type dat je gaat gebruiken bijvoorbeeld</typeparam>
    public partial class DatagridPanel<T> : UserControl
    {
        private IList<T> presentationObjects;
        private String[] columnNames;
        private String[] columnAlias;

        //public T SelectedObject
        //{
        //    get
        //    {
        //        if (dgvMain.CurrentRow != null)
        //        {
        //            List<T> templist = presentationObjects.ToList();
        //            if (dgvMain.SortedColumn != null)
        //            {
        //                templist.Sort(new Comparer<T>(dgvMain.SortedColumn.Name));
        //                if (dgvMain.SortOrder == SortOrder.Descending)
        //                    templist.Reverse();
        //            }
        //            return templist.ElementAt(dgvMain.CurrentRow.Index);
        //        }
        //        return default(T);
        //    }
        //}

        public IList<T> ObjectList
        {
            get
            {
                return presentationObjects;
            }
            set
            {
                presentationObjects = value;
                Init();
            }
        }

        public DataGridView Datagridview
        {
            get
            {
                return dgvMain;
            }
        }

        private BindingFlags bindingFlags = BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance;

        public DatagridPanel(IList<T> pObjects, String[] columnNames, String[] columAlias)
        {
            if (columnNames.Length != columAlias.Length)
                throw new ArgumentException("Number of columns and alias do not match");

            InitializeComponent();
            presentationObjects = pObjects;
            this.columnNames = columnNames;
            this.columnAlias = columAlias;
            dgvMain.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
            dgvMain.CurrentCellDirtyStateChanged += CommitEditMultipleRows;
            dgvMain.SelectionChanged+=dgvMain_SelectionChanged;
            dgvMain.DataError += DgvMainOnDataError;
            dgvMain.CellValidating += dgvMain_CellValidating;

            ContextMenuStrip mnu = new ContextMenuStrip();
            mnu.Items.Add("Keep selection", null, (sender, args) => rows = dgvMain.SelectedRows);
            mnu.Items.Add("Free selection", null, (sender, args) => rows = null);
            ContextMenuStrip = mnu;

            //if (!CheckProperties(typeof(T)))
            //    throw new System.Reflection.TargetException("Given column names do not match the attributes");

            //dgvMain.EditMode = DataGridViewEditMode.EditProgrammatically;

            Init();
        }

        void dgvMain_CellValidating(object sender, DataGridViewCellValidatingEventArgs e)
        {
            if (e.RowIndex > -1 && e.ColumnIndex > -1)
            {
                if (Datagridview.Columns[e.ColumnIndex].ValueType == typeof (Uri))
                {
                    e.Cancel = !Uri.IsWellFormedUriString(Convert.ToString(e.FormattedValue), UriKind.Absolute);
                }
            }
        }

        /// <summary>
        /// Handling exception thrown by cells and giving the appropiate message to the user
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void DgvMainOnDataError(object sender, DataGridViewDataErrorEventArgs e)
        {//todo fix multiselect messages
            e.ThrowException = false;
            e.Cancel = true;

            var exc = e.Exception;

            if (exc == null)
                return;

            if (exc is DuplicateNameException<T>)//todo localized
                MessageBox.Show(Language.Instance.UniquePropertyDuplicate);
            else if (exc is ArgumentNullException)
                MessageBox.Show(Language.Instance.PropertyEmpty);
            else if (exc is ArgumentException)
                MessageBox.Show(Language.Instance.PropertyInvalidFormat);
            else
                MessageBox.Show(exc.StackTrace + " | " + exc.Message);

        }

        void dgvMain_SelectionChanged(object sender, EventArgs e)
        {
            if (rows != null)
            {
                dgvMain.SelectionChanged -= dgvMain_SelectionChanged;
                dgvMain.ClearSelection();
                foreach (DataGridViewRow row in rows)
                {
                    row.Selected = true;
                }
                dgvMain.SelectionChanged += dgvMain_SelectionChanged;
            }
        }

        private DataGridViewSelectedRowCollection rows;

        void CommitEditMultipleRows(object sender, EventArgs e)
        {
            if (rows == null)
                return; 

            DataGridView dgv = (DataGridView)sender;
            DataGridViewCell cell = dgv.CurrentCell;
            if (cell.RowIndex >= 0)
            {
                // If checkbox value changed, copy it's value to all selectedrows
                object val = dgv.Rows[cell.RowIndex].Cells[cell.ColumnIndex].Value;

                foreach (DataGridViewRow row in rows)
                {
                    row.Cells[cell.ColumnIndex].Value = val;
                }
            }

            dgvMain.CommitEdit(DataGridViewDataErrorContexts.Commit);
        }

        public DatagridPanel(IList<T> pObjects, String[] columnNames)
            :this(pObjects, columnNames, columnNames)
        {
        }

        private void AddColumn(string columnName, string columnAlias)
        {
            DataGridViewColumn column = new DataGridViewTextBoxColumn {DataPropertyName = Name};
            column.Name = columnName;
            column.HeaderText = columnAlias;
            column.DataPropertyName = columnName;
            dgvMain.Columns.Add(column);
        }

        //private void AddRow(object[] columnInfo)
        //{
        //    dgvMain.Rows.Add(columnInfo);
        //}

        public void SetCellType(string column, DataGridViewColumn type, object datasource = null)
        {
            var context = dgvMain.Columns[column];
            if (context != null)
            {
                int index = context.Index;
                var dataGridViewComboBoxCell = type as DataGridViewComboBoxColumn;
                if (dataGridViewComboBoxCell != null)
                {
                    dataGridViewComboBoxCell.DataSource = datasource;
                    dataGridViewComboBoxCell.DisplayMember = "Key"; //todo make generic
                    dataGridViewComboBoxCell.ValueMember = "Value";
                }
                type.DataPropertyName = context.DataPropertyName;
                type.Name = context.Name;
                type.HeaderText = context.HeaderText;
                dgvMain.Columns.Insert(index, type);
                dgvMain.Columns.Remove(context);
            }
            Invalidate();
        }

        public override void Refresh()
        {
            if (dgvMain.DataSource != null && !(dgvMain.DataSource is IBindingList))
            {
                var temp = dgvMain.DataSource;
                dgvMain.DataSource = null;
                dgvMain.DataSource = temp; //todo dirty
            }
            base.Refresh();
        }

        private void Init()
        {
            dgvMain.DataSource = presentationObjects;
            dgvMain.Columns.Clear();
            dgvMain.AutoGenerateColumns = false;

            for (int i = 0; i < columnAlias.Length; i++)
            {
                AddColumn(columnNames[i], columnAlias[i]);
            }
        }
    }

    class DataGridPanelObjectWrapper<T>
    {
        public static List<DataGridPanelObjectWrapper<T>> WrapObjects(IEnumerable<T> list)
        {
            if (list == null)
                throw new ArgumentNullException("list");

            return list.Select(obj => new DataGridPanelObjectWrapper<T>(obj)).ToList();
        }   

        public DataGridPanelObjectWrapper(T obj)
        {
            Key = Convert.ToString(obj);
            Value = obj;
        }

        public string Key { get; set; }
        public T Value { get; set; }
    }

    class DataGridViewMaskedTextBoxColumn : DataGridViewTextBoxColumn
    {
        public DataGridViewMaskedTextBoxColumn(DataGridView parent)
        {
            //todo get DataGridViewChanged event
            parent.CellFormatting += DataGridView_CellFormatting;
        }

        void DataGridView_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
        {
            if (e.ColumnIndex > -1)
            {
                var column = DataGridView.Columns[e.ColumnIndex];
                var cell = DataGridView[e.ColumnIndex, e.RowIndex];

                if (!cell.IsInEditMode)
                {
                    if (column is DataGridViewMaskedTextBoxColumn)
                        e.Value = new String('•', Convert.ToString(e.Value).Length); //For displaying
                }
                else
                {
                    SetEditingControlPasswordChar(column is DataGridViewMaskedTextBoxColumn);
                        //For displaying while editing
                }
            }
        }

        private void SetEditingControlPasswordChar(bool enabled)
        {
            var editedCell = DataGridView.EditingControl as TextBox;
            if (editedCell != null)
            {
                editedCell.UseSystemPasswordChar = enabled;
            }
        }
    }

    public class CustomDataGridView : DataGridView
    {
        protected override void OnMouseDown(MouseEventArgs e)
        {
            // To save me from a headache, for some reason .NET keeps throwing IndexOutOfRangeExceptions when
            // clicking ColumnHeaders or the first row (even when using BindingLists)
            // IndexOutOfRangeException (Index -1 does not have a value)
            try
            {
                base.OnMouseDown(e);
            }
            catch (IndexOutOfRangeException)
            {//todo maybe rethrow if the message does not equals: Index -1 does not have a value
            }
        }
    }
}
